/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.flaviofaria.kenburnsview;

import ohos.agp.animation.Animator;
import ohos.agp.utils.RectFloat;

/**
 * Transition
 *
 * @since 2021.07.12
 */
public class Transition {
    /**
     * The rect the transition will start from.
     */
    private RectFloat mSrcRect;
    /**
     * The rect the transition will end at.
     */
    private RectFloat mDstRect;
    /**
     * Precomputed width difference between {@link #mSrcRect} and {@link #mSrcRect}.
     */
    private float mWidthDiff;
    /**
     * Precomputed height difference between {@link #mSrcRect} and {@link #mSrcRect}.
     */
    private float mHeightDiff;
    /**
     * Precomputed X offset between the center points of
     * {@link #mSrcRect} and {@link #mSrcRect}.
     */
    private float mCenterXDiff;
    /**
     * Precomputed Y offset between the center points of
     * {@link #mSrcRect} and {@link #mSrcRect}.
     */
    private float mCenterYDiff;
    /**
     * The duration of the transition in milliseconds. The default duration is 5000 ms.
     */
    private long mDuration;

    private Animator.TimelineCurve mInterpolator;

    /**
     * An intermediary rect that changes in every frame according
     * to the transition progress.
     */
    private final RectFloat mCurrentRect = new RectFloat();

    /**
     * Transition instance
     *
     * @param srcRect 原区域
     * @param dstRect 目标区域
     * @param duration 动画时间
     * @param interpolator 插值器
     * @throws IncompatibleRatioException 异常
     */
    public Transition(RectFloat srcRect, RectFloat dstRect, long duration, Animator.TimelineCurve interpolator) {
        if (!MathUtils.haveSameAspectRatio(srcRect, dstRect)) {
            throw new IncompatibleRatioException("Can't perform Ken Burns effect on rects with distinct aspect ratios!");
        }
        mSrcRect = srcRect;
        mDstRect = dstRect;
        mDuration = duration;
        mInterpolator = interpolator;

        /**
         * Precomputes a few variables to avoid doing it in onDraw().
         */
        mWidthDiff = dstRect.getWidth() - srcRect.getWidth();
        mHeightDiff = dstRect.getHeight() - srcRect.getHeight();
        mCenterXDiff = dstRect.getCenter().getPointX() - srcRect.getCenter().getPointX();
        mCenterYDiff = dstRect.getCenter().getPointY() - srcRect.getCenter().getPointY();
    }

    /**
     * Gets the rect that will take the scene when a Ken Burns transition starts.
     *
     * @return the rect that starts the transition.
     */
    public RectFloat getSourceRect() {
        return mSrcRect;
    }

    /**
     * Gets the rect that will take the scene when a Ken Burns transition ends.
     *
     * @return the rect that ends the transition.
     */
    public RectFloat getDestinyRect() {
        return mDstRect;
    }

    /**
     * Gets the current rect that represents the part of the image to take the scene
     * in the current frame.
     *
     * @param elapsedTime the elapsed time since this transition started.
     * @return RectFloat
     */
    public RectFloat getInterpolatedRect(long elapsedTime) {
        float elapsedTimeFraction = elapsedTime / (float) mDuration;
        float interpolationProgress = Math.min(elapsedTimeFraction, 1);
        float interpolation = mInterpolator.getCurvedTime(interpolationProgress);
        float currentWidth = mSrcRect.getWidth() + (interpolation * mWidthDiff);
        float currentHeight = mSrcRect.getHeight() + (interpolation * mHeightDiff);

        float currentCenterX = mSrcRect.getCenter().getPointX() + (interpolation * mCenterXDiff);
        float currentCenterY = mSrcRect.getCenter().getPointY() + (interpolation * mCenterYDiff);

        float left = currentCenterX - (currentWidth / 2);
        float top = currentCenterY - (currentHeight / 2);
        float right = left + currentWidth;
        float bottom = top + currentHeight;

        mCurrentRect.left = left;
        mCurrentRect.top = top;
        mCurrentRect.right = right;
        mCurrentRect.bottom = bottom;
        return mCurrentRect;
    }

    /**
     * Gets the duration of this transition.
     *
     * @return the duration, in milliseconds.
     */
    public long getDuration() {
        return mDuration;
    }
}
